using System;
using System.Collections.Concurrent;
using System.Net.Http;

namespace K3Cloud.WebApi.Core.IoC.Http;

/// <summary>
/// HTTP 客户端连接池管理器
/// 用于复用 HttpClient 实例，避免端口耗尽和性能问题
/// </summary>
public sealed class HttpClientPool : IDisposable
{
    private static readonly Lazy<HttpClientPool> _instance = new Lazy<HttpClientPool>(() => new HttpClientPool());
    
    /// <summary>
    /// 获取连接池单例实例
    /// </summary>
    public static HttpClientPool Instance => _instance.Value;

    private readonly ConcurrentDictionary<string, Lazy<System.Net.Http.HttpClient>> _clients;
    private readonly TimeSpan _defaultTimeout;
    private bool _disposed;

    /// <summary>
    /// 初始化连接池
    /// </summary>
    /// <param name="defaultTimeout">默认超时时间，默认为 100 秒</param>
    private HttpClientPool(TimeSpan? defaultTimeout = null)
    {
        _clients = new ConcurrentDictionary<string, Lazy<System.Net.Http.HttpClient>>();
        _defaultTimeout = defaultTimeout ?? TimeSpan.FromSeconds(100);
    }

    /// <summary>
    /// 获取或创建指定服务器地址的 HttpClient 实例
    /// </summary>
    /// <param name="baseUrl">服务器基础地址</param>
    /// <param name="timeout">超时时间，如果为 null 则使用默认值</param>
    /// <returns>HttpClient 实例</returns>
    public System.Net.Http.HttpClient GetOrCreate(string baseUrl, TimeSpan? timeout = null)
    {
        if (string.IsNullOrWhiteSpace(baseUrl))
        {
            throw new ArgumentException("服务器地址不能为空", nameof(baseUrl));
        }

        if (_disposed)
        {
            throw new ObjectDisposedException(nameof(HttpClientPool));
        }

        // 使用 baseUrl 作为键
        var key = NormalizeUrl(baseUrl);
        var effectiveTimeout = timeout ?? _defaultTimeout;

        var lazyClient = _clients.GetOrAdd(key, _ => new Lazy<System.Net.Http.HttpClient>(() =>
        {
            var handler = new HttpClientHandler
            {
                // 启用自动解压缩
                AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate,
                // 禁用代理以提高性能（如需代理请按需开启）
                UseProxy = false,
                // 允许自动重定向
                AllowAutoRedirect = true,
                MaxAutomaticRedirections = 5
            };

            var client = new System.Net.Http.HttpClient(handler, disposeHandler: true)
            {
                BaseAddress = new Uri(baseUrl),
                Timeout = effectiveTimeout
            };

            // 设置默认请求头
            client.DefaultRequestHeaders.ConnectionClose = false; // 保持连接复用
            client.DefaultRequestHeaders.Add("User-Agent", "K3Cloud.WebApi.Core.IoC/1.2");

            return client;
        }));

        return lazyClient.Value;
    }

    /// <summary>
    /// 移除指定服务器地址的 HttpClient 实例
    /// </summary>
    /// <param name="baseUrl">服务器基础地址</param>
    /// <returns>是否成功移除</returns>
    public bool Remove(string baseUrl)
    {
        if (string.IsNullOrWhiteSpace(baseUrl))
        {
            return false;
        }

        var key = NormalizeUrl(baseUrl);
        if (_clients.TryRemove(key, out var lazyClient) && lazyClient.IsValueCreated)
        {
            lazyClient.Value.Dispose();
            return true;
        }

        return false;
    }

    /// <summary>
    /// 清空连接池中的所有 HttpClient 实例
    /// </summary>
    public void Clear()
    {
        foreach (var kvp in _clients)
        {
            if (kvp.Value.IsValueCreated)
            {
                kvp.Value.Value.Dispose();
            }
        }
        _clients.Clear();
    }

    /// <summary>
    /// 获取连接池中的 HttpClient 实例数量
    /// </summary>
    public int Count => _clients.Count;

    /// <summary>
    /// 标准化 URL 格式
    /// </summary>
    private string NormalizeUrl(string url)
    {
        if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
            !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        {
            url = "http://" + url;
        }

        // 移除末尾的斜杠
        return url.TrimEnd('/');
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        Clear();
        _disposed = true;
    }
}
